home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
SciAn
/
src
/
ScianAnimation.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
36KB
|
1,574 lines
/*ScianAnimation.c
Eric Pepke
21 May 1993
Advanced keyframe animation for SciAn
*/
#include "Scian.h"
#include "ScianTypes.h"
#include "ScianIDs.h"
#include "ScianNames.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianDialogs.h"
#include "ScianScripts.h"
#include "ScianErrors.h"
#include "ScianSymbols.h"
#include "ScianEvents.h"
#include "ScianLists.h"
#include "ScianArrays.h"
#include "ScianControls.h"
#include "ScianButtons.h"
#include "ScianTextBoxes.h"
#include "ScianTitleBoxes.h"
#include "ScianIcons.h"
#include "ScianStyle.h"
#include "ScianObjFunctions.h"
#include "ScianDatabase.h"
#include "ScianColors.h"
#include "ScianObjFunctions.h"
#include "ScianTemplates.h"
#include "ScianTemplateHelper.h"
#include "ScianSnap.h"
#include "ScianAnimation.h"
#include "ScianTrackControls.h"
#include "ScianDraw.h"
#include "ScianMethods.h"
#define P2T(p) ((((p) - l) * (maxTime - minTime) / ((real) r - l)) + minTime)
#define T2P(t) floor(((((t) - minTime) * ((real) r - l) / (maxTime - minTime)) + l) + 0.5)
ObjPtr sequenceClass; /*Class of a sequence*/
ObjPtr trackClass; /*Class of a track within a sequence*/
ObjPtr subTrackClass; /*Class of a subtrack within a track*/
ObjPtr keyFrameClass; /*Class of a keyframe within a subtrack*/
char **betterVarNames = 0; /*Better variable names*/
int nBetterVarNames = 0; /*Number of better var names*/
#ifdef PROTO
static ObjPtr NewTrack(ObjPtr class, ObjPtr repObj)
#else
static ObjPtr NewTrack(class, repObj)
ObjPtr class;
ObjPtr repObj;
#endif
/*Creates a new track to control repObj*/
{
ObjPtr retVal;
retVal = NewObject(class ? class : trackClass, 0L);
SetVar(retVal, REPOBJ, repObj);
MakeVar(repObj, NAME);
SetVar(retVal, NAME, GetVar(repObj, NAME));
return retVal;
}
#ifdef PROTO
static ObjPtr NewSubTrack(ObjPtr class, ObjPtr repObj, char *name)
#else
static ObjPtr NewSubTrack(class, repObj, name)
ObjPtr class;
ObjPtr repObj;
char *name;
#endif
/*Creates a new subtrack to control repObj's variable group named name*/
{
ObjPtr retVal;
retVal = NewObject(class ? class : subTrackClass, 0L);
SetVar(retVal, REPOBJ, repObj);
SetVar(retVal, NAME, NewString(name));
return retVal;
}
#ifdef PROTO
static ObjPtr NewKeyFrame(int whichFrame, ObjPtr snapshot)
#else
static ObjPtr NewKeyFrame(whichFrame, snapshot)
int whichFrame;
ObjPtr snapshot;
#endif
/*Makes a new keyframe at time using snapshot.*/
{
ObjPtr retVal;
retVal = NewObject(keyFrameClass, 0L);
SetVar(retVal, WHICHFRAME, NewInt(whichFrame));
SetVar(retVal, SNAPSHOT, snapshot);
return retVal;
}
#ifdef PROTO
void InsertKeySnapshot(ObjPtr track, int whichFrame, ObjPtr snapshot)
#else
void InsertKeySnapshot(track, whichFrame, snapshot)
ObjPtr track, snapshot;
int whichFrame;
#endif
/*Inserts a key snapshot at frame whichFrame into track, or just updates
what's there.*/
{
ObjPtr keyframes;
ThingListPtr runner;
long index;
long dim;
ObjPtr *elements;
ObjPtr var;
keyframes = GetVar(track, KEYFRAMES);
if (!keyframes)
{
dim = 1;
keyframes = NewArray(AT_OBJECT, 1, &dim);
index = 0;
elements = ELEMENTS(keyframes);
elements[0] = NewKeyFrame(whichFrame, snapshot);
SetVar(track, KEYFRAMES, keyframes);
}
else
{
/*Search to see if there is already a keyframe there*/
index = SearchIntVar(keyframes, WHICHFRAME, whichFrame);
elements = ELEMENTS(keyframes);
if (index > 0)
{
/*See if the element just before is the one*/
MakeVar(elements[index - 1], WHICHFRAME);
var = GetVar(elements[index - 1], WHICHFRAME);
if (var && IsInt(var) && GetInt(var) == whichFrame)
{
/*Found it! Just change it's snapshot, touch the keyframe,
and return*/
SetVar(elements[index - 1], SNAPSHOT, snapshot);
SetVar(track, KEYFRAMES, keyframes);
return;
}
}
/*Insert keyframe where it belongs*/
keyframes = InsertInArray(keyframes,
NewKeyFrame(whichFrame, snapshot), index);
SetVar(track, KEYFRAMES, keyframes);
}
}
#ifdef PROTO
ObjPtr TrackObjectWithinSequence(ObjPtr sequence, ObjPtr object, ObjPtr trackClass)
#else
ObjPtr TrackObjectWithinSequence(sequence, object, trackClass)
ObjPtr sequence;
ObjPtr object;
ObjPtr trackClass;
#endif
/*Returns the track for object within sequence or adds one*/
{
ObjPtr tracks, track, var;
ObjPtr *elements;
long k, dim;
/*First find out if there is a track with that object*/
MakeVar(sequence, TRACKS);
tracks = GetVar(sequence, TRACKS);
if (tracks)
{
for (k = 0; k < DIMS(tracks)[0]; ++k)
{
var = GetObjectElement(tracks, &k);
if (GetVar(var, REPOBJ) == object)
{
return var;
}
}
}
/*Fell through, object not found. Create a new track*/
track = NewTrack(trackClass, object);
if (tracks)
{
/*Insert track in sorted place*/
var = GetStringVar("TrackObjectWithinSequence", object, NAME);
if (!var) return NULLOBJ;
k = SearchStringVar(tracks, NAME, GetString(var));
if (k < 0) return NULLOBJ;
tracks = InsertInArray(tracks, track, k);
}
else
{
/*Create single array to hold tracks*/
dim = 1;
tracks = NewArray(AT_OBJECT, 1, &dim);
elements = (ObjPtr *) ELEMENTS(tracks);
elements[0] = track;
}
SetVar(track, PARENT, sequence);
SetVar(sequence, TRACKS, tracks);
return track;
}
#ifdef PROTO
ObjPtr GetTrackedObjectWithinSequence(ObjPtr sequence, ObjPtr object)
#else
ObjPtr GetTrackedObjectWithinSequence(sequence, object)
ObjPtr sequence;
ObjPtr object;
#endif
/*Returns the track for object within sequence or returns NULLOBJ*/
{
ObjPtr tracks, track, var;
ObjPtr *elements;
long k, dim;
/*First find out if there is a track with that object*/
MakeVar(sequence, TRACKS);
tracks = GetVar(sequence, TRACKS);
if (tracks)
{
for (k = 0; k < DIMS(tracks)[0]; ++k)
{
var = GetObjectElement(tracks, &k);
if (GetVar(var, REPOBJ) == object)
{
return var;
}
}
}
/*Fell through, object not found.*/
return NULLOBJ;
}
#ifdef PROTO
ObjPtr TrackVarGroupWithinSequence(ObjPtr sequence, ObjPtr object, char *group, ObjPtr trackClass)
#else
ObjPtr TrackVarGroupWithinSequence(sequence, object, group, trackClass)
ObjPtr sequence;
ObjPtr object;
char *group;
ObjPtr trackClass;
#endif
/*Returns the track for vargroup group of object within sequence
or adds one*/
{
ObjPtr track, var, name;
ObjPtr subTracks, subTrack;
ObjPtr *elements;
long k, dim;
track = TrackObjectWithinSequence(sequence, object, NULLOBJ);
/*First find out if there is a subtrack for that group*/
MakeVar(track, SUBTRACKS);
subTracks = GetVar(track, SUBTRACKS);
if (subTracks)
{
for (k = 0; k < DIMS(subTracks)[0]; ++k)
{
var = GetObjectElement(subTracks, &k);
name = GetVar(var, NAME);
if (name && (0 == strcmp2(GetString(name), group)))
{
return var;
}
}
}
/*Fell through, object not found. Create a new track*/
subTrack = NewSubTrack(trackClass, object, group);
if (IsSelected(track)) Select(subTrack, true);
if (subTracks)
{
/*Insert track in sorted place*/
k = SearchStringVar(subTracks, NAME, group);
if (k < 0) return NULLOBJ;
subTracks = InsertInArray(subTracks, subTrack, k);
}
else
{
/*Create single array to hold tracks*/
dim = 1;
subTracks = NewArray(AT_OBJECT, 1, &dim);
elements = (ObjPtr *) ELEMENTS(subTracks);
elements[0] = subTrack;
}
SetVar(subTrack, PARENT, track);
SetVar(track, SUBTRACKS, subTracks);
/*Also need to touch TRACKS of sequence*/
SetVar(sequence, TRACKS, GetVar(sequence, TRACKS));
return subTrack;
}
#ifdef PROTO
ObjPtr GetTrackedVarGroupWithinSequence(ObjPtr sequence, ObjPtr object, char *group)
#else
ObjPtr GetTrackedVarGroupWithinSequence(sequence, object, group)
ObjPtr sequence;
ObjPtr object;
char *group;
#endif
/*Returns the track for vargroup group of object within sequence
or NULLOBJ*/
{
ObjPtr track, var, name;
ObjPtr subTracks, subTrack;
ObjPtr *elements;
long k, dim;
track = GetTrackedObjectWithinSequence(sequence, object);
if (!track) return NULLOBJ;
/*First find out if there is a subtrack for that group*/
MakeVar(track, SUBTRACKS);
subTracks = GetVar(track, SUBTRACKS);
if (subTracks)
{
for (k = 0; k < DIMS(subTracks)[0]; ++k)
{
var = GetObjectElement(subTracks, &k);
name = GetVar(var, NAME);
if (name && (0 == strcmp2(GetString(name), group)))
{
return var;
}
}
}
/*Fell through, object not found.*/
return NULLOBJ;
}
#if 1
static void toado(ObjPtr sequence, char *name)
{
ObjPtr temp;
temp = NewObject(NULLOBJ, 0L);
SetVar(temp, NAME, NewString(name));
TrackVarGroupWithinSequence(sequence, temp, "Guano", NULLOBJ);
TrackVarGroupWithinSequence(sequence, temp, "Bat", NULLOBJ);
}
#endif
#ifdef PROTO
ObjPtr NewSequence(char *name)
#else
ObjPtr NewSequence(name)
char *name;
#endif
/*Returns a new sequence with name, also shows controls*/
{
ObjPtr retVal;
/*UPDATE log command to make sequence*/
retVal = NewObject(sequenceClass, 0L);
SetVar(retVal, NAME, NewString(name));
AddObjToDatabase(retVal);
#if 0
/*DEBUG give it some tracks by default*/
{
toado(retVal, "And");
toado(retVal, "Did");
toado(retVal, "Those");
toado(retVal, "Teeth");
toado(retVal, "In");
toado(retVal, "Ancient");
toado(retVal, "Times");
toado(retVal, "Walk");
toado(retVal, "Upon");
toado(retVal, "England's");
toado(retVal, "Mountains");
toado(retVal, "Green?");
}
#endif
DeferMessage(retVal, SHOWCONTROLS);
return retVal;
}
#ifdef PROTO
void DoNewSequence(void)
#else
void DoNewSequence()
#endif
/*Asks the user for a name and makes the new sequence*/
{
ObjPtr name;
name = NewUniqueName("Sequence #1", CLASS_SEQUENCE);
NewSequence(GetString(name));
}
#ifdef PROTO
void ExpressTrack(ObjPtr track, int frame)
#else
void ExpressTrack(track, frame)
ObjPtr track;
int frame;
#endif
/*Expresses a track by adjusting all the variables it controls to frame*/
{
ObjPtr var;
ObjPtr *elements;
long k;
FuncTyp method;
MakeVar(track, SUBTRACKS);
var = GetVar(track, SUBTRACKS);
if (var)
{
elements = ELEMENTS(var);
for (k = 0; k < DIMS(var)[0]; ++k)
{
method = GetMethod(elements[k], EXPRESS);
if (method)
{
(*method)(elements[k], frame);
}
}
}
}
static ObjPtr ExpressDefaultSubTrack(track, frame)
ObjPtr track;
int frame;
/*Default method to express a variable group subtrack at frame*/
{
ObjPtr *elements;
long index;
ObjPtr keyFrames;
ObjPtr var;
ObjPtr frame1, frame2; /*Frames for interpolation*/
real weight;
int n1, n2;
ObjPtr newSnap;
/*Get the keyframes*/
keyFrames = GetVar(track, KEYFRAMES);
if (!keyFrames) return ObjFalse;
/*Find the index for this frame*/
index = SearchIntVar(keyFrames, WHICHFRAME, frame);
if (index <= 0)
{
/*Before first*/
return ObjFalse;
}
elements = ELEMENTS(keyFrames);
frame1 = elements[index - 1];
/*See if it's smack dab on a frame*/
var = GetIntVar("ExpressDefaultSubTrack", frame1, WHICHFRAME);
if (!var) return ObjFalse;
if (GetInt(var) == frame)
{
/*That's it*/
var = GetObjectVar("ExpressDefaultSubTrack", frame1, SNAPSHOT);
if (!var) return ObjFalse;
ApplySnapshot(var);
return;
}
if (index >= DIMS(keyFrames)[0])
{
/*After the last*/
return ObjFalse;
}
/*Have two keyframes*/
frame2 = elements[index];
/*Calculate weight*/
var = GetVar(frame1, WHICHFRAME);
if (!var) return ObjFalse;
n1 = GetInt(var);
var = GetVar(frame2, WHICHFRAME);
if (!var) return ObjFalse;
n2 = GetInt(var);
weight = ((real) (frame - n1)) / ((real) (n2 - n1));
newSnap = InterpSnapshots(GetVar(frame1, SNAPSHOT),
GetVar(frame2, SNAPSHOT), weight);
ApplySnapshot(newSnap);
}
#ifdef PROTO
void ExpressSequence(ObjPtr sequence)
#else
void ExpressSequence(sequence)
ObjPtr sequence;
#endif
/*Expresses a sequence by adjusting all the variables it controls to
the current time*/
{
ObjPtr var;
real time;
int frame;
ObjPtr *elements;
long k;
var = GetRealVar("ExpressSequence", sequence, TIME);
if (!var) return;
time = GetReal(var);
MakeVar(sequence, FRAMERATE);
var = GetRealVar("ExpressSequence", sequence, FRAMERATE);
if (!var) return;
frame = (int) floor(time * GetReal(var) + 0.5);
InhibitLogging(true);
MakeVar(sequence, TRACKS);
var = GetVar(sequence, TRACKS);
if (var)
{
elements = ELEMENTS(var);
for (k = 0; k < DIMS(var)[0]; ++k)
{
ExpressTrack(elements[k], frame);
}
}
InhibitLogging(false);
}
static ObjPtr ChangeSequenceTime(control)
ObjPtr control;
/*Changes the time in a sequence in response to control*/
{
ObjPtr repObj;
repObj = GetObjectVar("ChangedSequenceTime", control, REPOBJ);
if (repObj)
{
SetVar(repObj, TIME, GetValue(control));
ExpressSequence(repObj);
}
return ObjTrue;
}
static ObjPtr ShowSequenceControls(object, windowName)
ObjPtr object;
char *windowName;
/*Makes a new control window to control sequence object*/
{
WinInfoPtr controlWindow;
ObjPtr var;
ObjPtr panel;
ObjPtr contents;
ObjPtr control;
int left, right, bottom, top, width;
WinInfoPtr dialogExists;
GetTemplateBounds(SequenceTemplate, "Panel", &left, &right, &bottom, &top);
dialogExists = DialogExists((WinInfoPtr) object, NewString("Controls"));
controlWindow = GetDialog((WinInfoPtr) object, NewString("Controls"), windowName,
(right - left), (top - bottom), 1280, 1024, WINUI);
if (!dialogExists)
{
SetVar((ObjPtr) controlWindow, REPOBJ, object);
/*Put in a help string*/
SetVar((ObjPtr) controlWindow, HELPSTRING,
NewString("This window shows controls for a an animation sequence. Using the controls in this \
window, you can define keyframes and produce mini-movies."));
/*Add in a panel*/
panel = TemplatePanel(SequenceTemplate, "Panel");
if (!panel)
{
return ObjFalse;
}
contents = GetVar((ObjPtr) controlWindow, CONTENTS);
PrefixList(contents, panel);
SetVar(panel, PARENT, (ObjPtr) controlWindow);
contents = GetVar(panel, CONTENTS);
/*Make a new track control to hold the tracks*/
control = TemplateTrackControl(SequenceTemplate, "Animation Tracks",
0.0, 10.0);
SetVar(control, PARENT, panel);
SetVar(control, REPOBJ, object);
PrefixList(contents, control);
SetVar(control, HELPSTRING,
NewString("This control allows you to do lots of neat stuff with \
animation."));
SetVar(control, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT + STICKYBOTTOM + STICKYTOP));
SetMethod(control, CHANGEDVALUE, ChangeSequenceTime);
control = TemplateCheckBox(SequenceTemplate, "Automatically Add New Keyframes", false);
if (control)
{
SetVar(control, PARENT, panel);
PrefixList(contents, control);
SetVar(control, HELPSTRING,
NewString("When this check box is on, anything you do to any window or visualization \
object will automatically add new keyframes at the time given by the track cursor."));
SetVar(control, INHIBITRECORDING, ObjTrue);
AssocDirectControlWithVar(control, object, RECORDING);
SetVar(control, STICKINESS, NewInt(STICKYLEFT + STICKYBOTTOM));
}
}
return (ObjPtr) controlWindow;
}
static ObjPtr DrawTrackName(track, l, r, b, t)
ObjPtr track;
int l, r, b, t;
/*Draws a track name within l, r, b, t*/
{
int texty;
ObjPtr name;
ObjPtr subTracks;
int k;
ObjPtr *elements;
MakeVar(track, APPEARANCE);
texty = t - TC_CELLHEIGHT / 2 - TCFONTSIZE / 2 + TC_TEXTBUP;
FillUIRect(l, r, t - TC_CELLHEIGHT, t, IsSelected(track) ? UIYELLOW : UIGRAY62);
SetUIColor(UIBLACK);
name = GetVar(track, NAME);
if (name)
{
DrawAString(LEFTALIGN, l + 1 + TCTEXTLOFF,
texty, GetString(name));
}
else
{
DrawAString(LEFTALIGN, l + 1 + TCTEXTLOFF,
texty, "?");
}
if (GetPredicate(track, EXPANDEDP))
{
/*Draw the subtracks*/
ObjPtr var;
FuncTyp method;
MakeVar(track, SUBTRACKS);
subTracks = GetVar(track, SUBTRACKS);
if (subTracks)
{
t -= TC_CELLHEIGHT;
elements = ELEMENTS(subTracks);
for (k = 0; k < DIMS(subTracks)[0]; ++k)
{
MakeVar(elements[k], TRACKHEIGHT);
var = GetVar(elements[k], TRACKHEIGHT);
if (var)
{
b = t - GetInt(var);
method = GetMethod(elements[k], DRAWNAME);
if (method)
{
(*method)(elements[k], l, r, b, t);
}
t = b;
}
}
}
}
return ObjTrue;
}
#ifdef PROTO
static void DrawSubTrackMarker(int x, int y)
#else
static void DrawSubTrackMarker(x, y)
int x, y;
#endif
/*Draws a subtrack keyframe marker at x, y*/
{
int left, right, bottom, top;
left = x - TC_STMARKHWIDTH;
right = x + TC_STMARKHWIDTH + 1;
bottom = y - TC_STMARKHHEIGHT;
top = y + TC_STMARKHHEIGHT + 1;
/*face*/
FillUIRect(left + TC_STMARKEDGE, right - TC_STMARKEDGE,
bottom + TC_STMARKEDGE, top - TC_STMARKEDGE, UIBACKGROUND);
/* bottom */
SetUIColor(UIBOTTOMEDGE);
FillIntQuad( left, bottom,
right, bottom,
right - TC_STMARKEDGE, bottom + TC_STMARKEDGE,
left + TC_STMARKEDGE, bottom + TC_STMARKEDGE);
/* left */
SetUIColor(UILEFTEDGE);
FillIntQuad( left, bottom,
left + TC_STMARKEDGE, bottom + TC_STMARKEDGE,
left + TC_STMARKEDGE, top - TC_STMARKEDGE,
left, top);
/* right */
SetUIColor(UIRIGHTEDGE);
FillIntQuad( right - TC_STMARKEDGE, bottom + TC_STMARKEDGE,
right, bottom,
right, top,
right - TC_STMARKEDGE, top - TC_STMARKEDGE);
/* top */
SetUIColor(UITOPEDGE);
FillIntQuad( left + TC_STMARKEDGE, top - TC_STMARKEDGE,
right - TC_STMARKEDGE, top - TC_STMARKEDGE,
right, top,
left, top);
}
#ifdef PROTO
static void DrawTrackMarker(int x, int y)
#else
static void DrawTrackMarker(x, y)
int x, y;
#endif
/*Draws a subtrack keyframe marker at x, y*/
{
int left, right, bottom, top;
left = x - TC_STMARKHWIDTH;
right = x + TC_STMARKHWIDTH + 1;
bottom = y - TC_STMARKHHEIGHT;
top = y + TC_STMARKHHEIGHT + 1;
/* face */
FillUIRect(left, right, bottom, top - 1, UIBACKGROUND);
/* top */
DrawUILine(left, top - 1, right, top - 1, UISHARPTOPEDGE);
/* right */
DrawUILine(right, top - 1, right, bottom, UISHARPRIGHTEDGE);
/* left */
DrawUILine(left - 1, bottom, left - 1, top - 1, UISHARPLEFTEDGE);
/* bottom */
DrawUILine(left, bottom, right, bottom, UISHARPBOTTOMEDGE);
/* line */
DrawUILine(x, bottom + 1, x, top - 2, UIBLACK);
}
static ObjPtr DrawTrackLine(track, l, r, b, t, minTime, maxTime)
ObjPtr track;
int l, r, b, t;
real minTime, maxTime;
/*Draws a track name within l, r, b, t between minTime and maxTime*/
{
int middle;
ObjPtr name, sequence;
ObjPtr *subTracks, *keyframes;
int k;
ObjPtr var, keyframesVar;
int nSubtracks, nKeyframes;
real frameWidth;
int pixel, curFrame, testFrame, bestFrame;
int index;
Bool anyToDraw;
real time;
MakeVar(track, APPEARANCE);
middle = t - TC_CELLHEIGHT / 2;
#if 0
DrawUILine(l, middle + 1, r, middle + 1, UIYELLOW);
DrawUILine(l, middle, r, middle, UIYELLOW);
#endif
/*Get the sequence that owns this track -> sequence*/
sequence = GetObjectVar("DrawTrackLine", track, PARENT);
if (!sequence) return ObjFalse;
/*Get the frame width for converting from time to pixels*/
MakeVar(sequence, FRAMERATE);
var = GetRealVar("DrawSubTrackLine", sequence, FRAMERATE);
if (var)
{
frameWidth = 1.0 / GetReal(var);
}
else
{
frameWidth = 1.0 / 30.0;
}
/*Get the subtracks*/
MakeVar(track, SUBTRACKS);
var = GetVar(track, SUBTRACKS);
if (var)
{
subTracks = ELEMENTS(var);
nSubtracks = DIMS(var)[0];
}
else
{
subTracks = (ObjPtr *) 0;
}
/*Get starting pixel*/
pixel = l - TC_STMARKHWIDTH;
/*Get starting frame*/
curFrame = floor(P2T(pixel) / frameWidth + 0.5);
if (curFrame < 0) curFrame = 0;
if (subTracks)
{
/*Draw the track summary*/
/*Go through all the keyframes, setting up indices*/
for (k = 0; k < nSubtracks; ++k)
{
keyframesVar = GetVar(subTracks[k], KEYFRAMES);
if (keyframesVar)
{
keyframes = ELEMENTS(keyframesVar);
nKeyframes = DIMS(keyframesVar)[0];
/*Get the index of the first keyframe to draw*/
index = SearchIntVar(keyframesVar, WHICHFRAME, curFrame);
if (index > 0) --index;
/*Stick it into the keyframe*/
SetVar(keyframesVar, INDEX, NewInt(index));
}
}
do
{
/*Find keyframe which has lowest WHICHFRAME greater than or
equal to curFrame, automatically advancing anything less
than curFrame*/
bestFrame = 999999999;
anyToDraw = false;
for (k = 0; k < nSubtracks; ++k)
{
keyframesVar = GetVar(subTracks[k], KEYFRAMES);
if (keyframesVar)
{
keyframes = ELEMENTS(keyframesVar);
nKeyframes = DIMS(keyframesVar)[0];
var = GetIntVar("DrawTrackLine", keyframesVar, INDEX);
if (!var) continue;
index = GetInt(var);
if (index >= nKeyframes) continue;
var = GetIntVar("DrawTrackLine", keyframes[index], WHICHFRAME);
if (!var) continue;
while (index < nKeyframes && (testFrame = GetInt(var)) < curFrame)
{
++index;
SetVar(keyframesVar, INDEX, NewInt(index));
if (index < nKeyframes)
{
var = GetIntVar("DrawTrackLine", keyframes[index], WHICHFRAME);
}
}
if (index >= nKeyframes) continue;
bestFrame = MIN(bestFrame, testFrame);
anyToDraw = true;
}
}
/*Now we should have a bestFrame*/
if (anyToDraw)
{
/*Print out the bestFrame*/
time = bestFrame * frameWidth;
pixel = T2P(time);
DrawSubTrackMarker(pixel, middle);
/*Go to next frame*/
curFrame = bestFrame + 1;
}
} while (anyToDraw && pixel < r + TC_STMARKHWIDTH);
}
if (GetPredicate(track, EXPANDEDP))
{
/*Draw the subtracks*/
FuncTyp method;
if (subTracks)
{
t -= TC_CELLHEIGHT;
for (k = 0; k < nSubtracks; ++k)
{
MakeVar(subTracks[k], TRACKHEIGHT);
var = GetVar(subTracks[k], TRACKHEIGHT);
if (var)
{
b = t - GetInt(var);
method = GetMethod(subTracks[k], DRAWTRACK);
if (method)
{
(*method)(subTracks[k], l, r, b, t, minTime, maxTime);
}
t = b;
}
}
}
}
return ObjTrue;
}
static ObjPtr DrawSubTrackLine(track, l, r, b, t, minTime, maxTime)
ObjPtr track;
int l, r, b, t;
real minTime, maxTime;
/*Draws a subtrack within l, r, b, t between minTime and maxTime*/
{
int middle;
ObjPtr subTracks;
ObjPtr keyframes;
ObjPtr sequence;
ObjPtr *elements;
int pixel;
real testTime;
real time;
int testFrame;
real frameWidth;
int curFrame, nextFrame;
long index;
int startPixel;
ObjPtr var;
ObjPtr snap1, snap2;
/*Get the elements to draw*/
keyframes = GetVar(track, KEYFRAMES);
if (!keyframes) return ObjTrue;
elements = ELEMENTS(keyframes);
/*Get middle of cell for drawing things*/
middle = t - TC_CELLHEIGHT / 2;
/*Get the sequence that owns this subtrack -> track -> sequence*/
sequence = GetObjectVar("DrawSubTrackLine", track, PARENT);
if (!sequence) return ObjFalse;
sequence = GetObjectVar("DrawSubTrackLine", sequence, PARENT);
if (!sequence) return ObjFalse;
/*Get the frame width for converting from time to pixels*/
MakeVar(sequence, FRAMERATE);
var = GetRealVar("DrawSubTrackLine", sequence, FRAMERATE);
if (var)
{
frameWidth = 1.0 / GetReal(var);
}
else
{
frameWidth = 1.0 / 30.0;
}
/*Get starting pixel*/
pixel = l - TC_STMARKHWIDTH;
/*Get starting frame*/
curFrame = floor(P2T(pixel) / frameWidth + 0.5);
if (curFrame < 0) curFrame = 0;
/*Get the index of the first keyframe to draw*/
index = SearchIntVar(keyframes, WHICHFRAME, curFrame);
if (index > 0) --index;
var = GetIntVar("DrawSubTrackLine", elements[index], WHICHFRAME);
if (!var)
{
return ObjFalse;
}
curFrame = GetInt(var);
do
{
/*Get the next frame*/
if (index + 1 >= DIMS(keyframes)[0])
{
nextFrame = -1;
}
else
{
var = GetIntVar("DrawSubTrackLine", elements[index + 1], WHICHFRAME);
if (!var)
{
return ObjFalse;
}
nextFrame = GetInt(var);
}
/*Print out the curFrame*/
time = curFrame * frameWidth;
pixel = T2P(time);
DrawSubTrackMarker(pixel, middle);
if (nextFrame < 0)
{
/*That's it, don't bother any more*/
break;
}
/*Draw the line to the next frame*/
startPixel = pixel + TC_STMARKHWIDTH + 1;
time = nextFrame * frameWidth;
pixel = T2P(time);
snap1 = GetVar(elements[index], SNAPSHOT);
snap2 = GetVar(elements[index + 1], SNAPSHOT);
if (startPixel < pixel - TC_STMARKHWIDTH)
{
/*Draw a continuation line*/
FillUIRect(startPixel, pixel - TC_STMARKHWIDTH,
middle - 1, middle + 1,
EqualSnapshots(snap1, snap2) ? UIGRAY50 : UIBLUE);
}
/*Advance index and curFrame*/
curFrame = nextFrame;
++index;
} while (nextFrame >= 0 && pixel < r + TC_STMARKHWIDTH);
return ObjTrue;
}
static ObjPtr DrawSubTrackName(track, l, r, b, t)
ObjPtr track;
int l, r, b, t;
/*Draws a subtrack name within l, r, b, t*/
{
int texty;
ObjPtr name;
ObjPtr subTracks;
int k;
ObjPtr *elements;
MakeVar(track, APPEARANCE);
texty = t - TC_CELLHEIGHT / 2 - TCFONTSIZE / 2;
FillUIRect(l, r, t - TC_CELLHEIGHT, t, IsSelected(track) ? UIYELLOW : UIGRAY62);
SetUIColor(UIBLACK);
name = GetVar(track, NAME);
if (name)
{
DrawAString(LEFTALIGN, l + 1 + TCSUBTEXTLOFF,
texty, GetString(name));
}
else
{
DrawAString(LEFTALIGN, l + 1 + TCSUBTEXTLOFF,
texty, "?");
}
return ObjTrue;
}
static ObjPtr MakeTrackAppearance(track)
ObjPtr track;
/*Makes a track's appearance*/
{
ImInvalid(track);
SetVar(track, APPEARANCE, ObjTrue);
return ObjTrue;
}
static ObjPtr MakeSubTrackAppearance(track)
ObjPtr track;
/*Makes a subtrack's appearance*/
{
ImInvalid(track);
SetVar(track, APPEARANCE, ObjTrue);
return ObjTrue;
}
static ObjPtr PressTrackName(track, x, y, flags)
ObjPtr track;
int x, y;
long flags;
/*Press in a track control name section. y is negative from top*/
{
ObjPtr parent;
if (y > -TC_CELLHEIGHT)
{
/*It's a press in the main name*/
if (TOOL(flags) == T_HELP)
{
ContextHelp(track);
return ObjTrue;
}
if (TOOL(flags) == T_ROTATE)
{
flags |= F_EXTEND;
}
if (flags & F_EXTEND)
{
Select(track, IsSelected(track) ? false : true);
}
else
{
if (!IsSelected(track))
{
DeselectAll();
Select(track, true);
}
if (flags & F_DOUBLECLICK)
{
/*Expand or contract*/
SetVar(track, EXPANDEDP, GetPredicate(track, EXPANDEDP) ? ObjFalse : ObjTrue);
/*Have to touch parent's TRACKS*/
parent = GetObjectVar("PressTrackName", track, PARENT);
if (parent)
{
SetVar(parent, TRACKS, GetVar(parent, TRACKS));
}
/*DIKEO log*/
}
}
}
else if (GetPredicate(track, EXPANDEDP))
{
/*It may be a press in a subtrack*/
int ty;
ObjPtr var;
ObjPtr subTracks;
int height;
long k;
ObjPtr *elements;
ty = - TC_CELLHEIGHT;
subTracks = GetVar(track, SUBTRACKS);
if (subTracks)
{
elements = ELEMENTS(subTracks);
for (k = 0; k < DIMS(subTracks)[0]; ++k)
{
MakeVar(elements[k], TRACKHEIGHT);
var = GetVar(elements[k], TRACKHEIGHT);
if (var)
{
height = GetInt(var);
if ((ty - height) < y &&
(ty >= y)) break;
ty -= height;
}
}
if (k < DIMS(subTracks)[0])
{
/*It is a press in a subtrack*/
FuncTyp method;
method = GetMethod(elements[k], PRESSNAME);
if (method)
{
(*method)(elements[k], x, y - ty, flags);
}
}
}
}
return ObjTrue;
}
static ObjPtr PressSubTrackName(track, x, y, flags)
ObjPtr track;
int x, y;
long flags;
/*Press in a subtrack control name section. y is negative from top*/
{
ObjPtr parent;
if (TOOL(flags) == T_HELP)
{
ContextHelp(track);
return ObjTrue;
}
if (TOOL(flags) == T_ROTATE)
{
flags |= F_EXTEND;
}
if (flags & F_EXTEND)
{
Select(track, IsSelected(track) ? false : true);
}
else
{
if (!IsSelected(track))
{
DeselectAll();
Select(track, true);
}
}
/*Make its parent invalid*/
parent = GetVar(track, PARENT);
SetVar(parent, APPEARANCE, ObjTrue);
return ObjTrue;
}
static ObjPtr MakeTrackHeight(track)
ObjPtr track;
/*Makes a track's TRACKHEIGHT*/
{
int height;
height = TC_CELLHEIGHT;
if (GetPredicate(track, EXPANDEDP))
{
ObjPtr subTracks;
MakeVar(track, SUBTRACKS);
subTracks = GetVar(track, SUBTRACKS);
if (subTracks)
{
ObjPtr *elements;
ObjPtr var;
int k;
elements = ELEMENTS(subTracks);
for (k = 0; k < DIMS(subTracks)[0]; ++k)
{
MakeVar(elements[k], TRACKHEIGHT);
var = GetVar(elements[k], TRACKHEIGHT);
if (var)
{
height += GetInt(var);
}
}
}
}
SetVar(track, TRACKHEIGHT, NewInt(height));
return ObjTrue;
}
static ObjPtr MakeSubTrackHeight(track)
ObjPtr track;
/*Makes a subtrack's TRACKHEIGHT*/
{
SetVar(track, TRACKHEIGHT, NewInt(TC_CELLHEIGHT));
return ObjTrue;
}
static ObjPtr SelectTrack(track, whether)
ObjPtr track;
Bool whether;
/*Extra stuff for selecting a track; selects all its subtracks*/
{
ObjPtr subTracks;
ObjPtr *elements;
long k;
subTracks = GetVar(track, SUBTRACKS);
if (subTracks)
{
elements = ELEMENTS(subTracks);
for (k = 0; k < DIMS(subTracks)[0]; ++k)
{
Select(elements[k], whether);
}
}
}
#ifdef PROTO
void DefineBetterVarName(NameTyp var, char *name)
#else
void DefineBetterVarName(var, name)
NameTyp var;
char *name;
#endif
/*Defines a better var name for variable var as name*/
{
int k;
if (var >= nBetterVarNames)
{
if (betterVarNames)
{
betterVarNames = (char **) Realloc(betterVarNames, (var + 1) * sizeof(char *));
}
else
{
betterVarNames = (char **) Alloc((var + 1) * sizeof(char *));
}
for (k = nBetterVarNames; k <= var; ++k)
{
betterVarNames[k] = NULL;
}
nBetterVarNames = var + 1;
}
SAFEFREE(betterVarNames[var]);
betterVarNames[var] = (char *) Alloc(strlen(name) + 1);
strcpy(betterVarNames[var], name);
}
#ifdef PROTO
char *GetGoodVarName(NameTyp var)
#else
char *GetGoodVarName(var)
NameTyp var;
#endif
/*Gets a good var name for variable var and returns it. Return value is
volatile and disappears after next call to GetGoodVarName.*/
{
char *name;
static char processedName[300];
int k;
if (var < nBetterVarNames && betterVarNames[var])
{
strcpy(processedName, betterVarNames[var]);
}
else
{
name = GetInternalString(var);
strcpy(processedName, name);
for (k = 1; processedName[k]; ++k)
{
processedName[k] = tolower(processedName[k]);
}
}
return processedName;
}
#ifdef PROTO
void RecordObjectVar(ObjPtr object, NameTyp whichVar)
#else
void RecordObjectVar(object, whichVar)
ObjPtr object;
NameTyp whichVar;
#endif
/*Records the current value of an object var in recording sequences*/
{
ObjPtr keyList, result;
keyList = NewList();
PostfixList(keyList, NewSymbol(CLASSID));
PostfixList(keyList, NewInt(CLASS_SEQUENCE));
PostfixList(keyList, NewSymbol(RECORDING));
PostfixList(keyList, ObjTrue);
result = SearchDatabase(keyList);
if (result && LISTOF(result))
{
ThingListPtr runner;
char *name;
runner = LISTOF(result);
while (runner)
{
ObjPtr track, snapshot;
real time;
real frameWidth;
int whichFrame;
ObjPtr var;
name = GetGoodVarName(whichVar);
track = TrackVarGroupWithinSequence(runner -> thing, object, name, NULLOBJ);
if (track)
{
MakeVar(runner -> thing, FRAMERATE);
var = GetVar(runner -> thing, FRAMERATE);
if (var)
{
frameWidth = 1.0 / GetReal(var);
}
else
{
frameWidth = 1.0 / 30.0;
}
var = GetVar(runner -> thing, TIME);
if (var)
{
time = GetReal(var);
}
else
{
time = 0.0;
}
whichFrame = (int) floor(time / frameWidth + 0.5);
snapshot = TakeSingleVarSnapshot(object, whichVar);
InsertKeySnapshot(track, whichFrame, snapshot);
}
runner = runner -> next;
}
}
}
#ifdef PROTO
ObjPtr TrackBoundsInvalid(ObjPtr object, unsigned long changeCount)
#else
ObjPtr TrackBoundsInvalid(object, changeCount)
ObjPtr object;
unsigned long changeCount;
#endif
/*For an track, tests to see if it needs drawing. Returns
NULLOBJ if it does not
ObjTrue it it needs to be redrawn but does not know where
In order for an object to be declared invalid, its changed bounds
must have been changed more recently than changeCount
*/
{
ObjPtr subTracks;
MakeVar(object, APPEARANCE);
MakeVar(object, CHANGEDBOUNDS);
if (GetVarChangeCount(object, CHANGEDBOUNDS) > changeCount)
{
/*Object is not good, so return true*/
return ObjTrue;
}
MakeVar(object, SUBTRACKS);
subTracks = GetVar(object, SUBTRACKS);
if (subTracks && IsObjArray(subTracks))
{
/*Still, maybe some of the contents need to be drawn*/
ObjPtr *elements;
long k;
elements = ELEMENTS(subTracks);
for (k = 0; k < DIMS(subTracks)[0]; ++k)
{
if (BoundsInvalid(elements[k], changeCount))
{
return ObjTrue;
}
}
}
return NULLOBJ;
}
#ifdef PROTO
void InitAnimation(void)
#else
void InitAnimation()
#endif
/*Initializes the animations*/
{
sequenceClass = NewObject(NULLOBJ, 0L);
SetVar(sequenceClass, CLASSID, NewInt(CLASS_SEQUENCE));
SetMethod(sequenceClass, SHOWCONTROLS, NewControlWindow);
SetMethod(sequenceClass, NEWCTLWINDOW, ShowSequenceControls);
SetVar(sequenceClass, RECORDING, ObjFalse);
SetVar(sequenceClass, TIME, NewReal(0.0));
SetVar(sequenceClass, FRAMERATE, NewReal(30.0));
AddToReferenceList(sequenceClass);
trackClass = NewObject(NULLOBJ, 0L);
AddToReferenceList(trackClass);
DeclareDependency(trackClass, TRACKHEIGHT, EXPANDEDP);
DeclareDependency(trackClass, TRACKHEIGHT, CHANGED);
DeclareDependency(trackClass, TRACKHEIGHT, SUBTRACKS);
SetMethod(trackClass, TRACKHEIGHT, MakeTrackHeight);
SetMethod(trackClass, DRAWNAME, DrawTrackName);
SetMethod(trackClass, DRAWTRACK, DrawTrackLine);
DeclareDependency(trackClass, APPEARANCE, SELECTED);
DeclareDependency(trackClass, APPEARANCE, TRACKHEIGHT);
DeclareDependency(trackClass, APPEARANCE, CHANGED);
DeclareDependency(trackClass, APPEARANCE, EXPANDEDP);
SetMethod(trackClass, APPEARANCE, MakeTrackAppearance);
SetMethod(trackClass, PRESSNAME, PressTrackName);
SetMethod(trackClass, SELECT, SelectTrack);
SetMethod(trackClass, BOUNDSINVALID, TrackBoundsInvalid);
subTrackClass = NewObject(NULLOBJ, 0L);
AddToReferenceList(subTrackClass);
SetMethod(subTrackClass, TRACKHEIGHT, MakeSubTrackHeight);
SetMethod(subTrackClass, DRAWNAME, DrawSubTrackName);
SetMethod(subTrackClass, PRESSNAME, PressSubTrackName);
DeclareDependency(subTrackClass, APPEARANCE, KEYFRAMES);
DeclareDependency(subTrackClass, APPEARANCE, CHANGED);
SetMethod(subTrackClass, APPEARANCE, MakeSubTrackAppearance);
SetMethod(subTrackClass, DRAWTRACK, DrawSubTrackLine);
SetMethod(subTrackClass, EXPRESS, ExpressDefaultSubTrack);
keyFrameClass = NewObject(NULLOBJ, 0L);
AddToReferenceList(keyFrameClass);
DefineBetterVarName(ROTQUAT, "Rotation");
DefineBetterVarName(FOCUSDIST, "Focus Distance");
DefineBetterVarName(ISOVAL, "Isovalue");
}
#ifdef PROTO
void KillAnimation(void)
#else
void KillAnimation()
#endif
/*Kills animation*/
{
int k;
AddToReferenceList(keyFrameClass);
RemoveFromReferenceList(subTrackClass);
RemoveFromReferenceList(trackClass);
RemoveFromReferenceList(sequenceClass);
for (k = 0; k < nBetterVarNames; ++k)
{
SAFEFREE(betterVarNames[k]);
}
SAFEFREE(betterVarNames);
}